﻿/*
**  2D世界
*/
if(!$.Engine.Dynamic || typeof $.Engine.Dynamic === "undefined") $.Engine.Dynamic = {};
if (!$.Engine.Dynamic.World || typeof $.Engine.Dynamic.World === "undefined") {
  $.Engine.Dynamic.World = function (worldAABB, gravity, doSleep) {
    this.step = new $.Engine.Dynamic.TimeStep();
    this.m_contactManager = new $.Engine.Dynamic.ContactManager();

    this.m_listener = null;
    this.m_filter = new $.Engine.Dynamic.CollisionFilter();

    this.m_bodyList = null;
    this.m_contactList = null;
    this.m_jointList = null;

    this.m_bodyCount = 0;
    this.m_contactCount = 0;
    this.m_jointCount = 0;

    this.m_bodyDestroyList = null;

    this.m_allowSleep = doSleep;

    this.m_gravity = gravity;

    this.m_contactManager.m_world = this;
    this.m_broadPhase = new $.Engine.Collision.BroadPhase(worldAABB, this.m_contactManager);
  };
}

Object.extend($.Engine.Dynamic.World.prototype, {

  // Set a callback to notify you when a joint is implicitly destroyed
  // when an attached body is destroyed.
  SetListener: function (listener) {
    this.m_listener = listener;
  },

  // Register a collision filter to provide specific control over collision.
  // Otherwise the default filter is used (b2CollisionFilter).
  SetFilter: function (filter) {
    this.m_filter = filter;
  },

  // Create and destroy rigid bodies. Destruction is deferred until the
  // the next call to this.Step. This is done so that bodies may be destroyed
  // while you iterate through the contact list.
  CreateBody: function (def) {
    //void* mem = this.m_blockAllocator.Allocate(sizeof(b2Body));
    var b = new $.Engine.Dynamic.Body(def, this);
    b.m_prev = null;

    b.m_next = this.m_bodyList;
    if (this.m_bodyList) {
      this.m_bodyList.m_prev = b;
    }
    this.m_bodyList = b;
    ++this.m_bodyCount;

    return b;
  },
  // Body destruction is deferred to make contact processing more robust.
  DestroyBody: function (b) {

    if (b.m_flags & $.Engine.Dynamic.Body.e_destroyFlag) {
      return;
    }

    // Remove from normal body list.
    if (b.m_prev) {
      b.m_prev.m_next = b.m_next;
    }

    if (b.m_next) {
      b.m_next.m_prev = b.m_prev;
    }

    if (b == this.m_bodyList) {
      this.m_bodyList = b.m_next;
    }

    b.m_flags |= $.Engine.Dynamic.Body.e_destroyFlag;
    //$.Engine.Settings.b2Assert(this.m_bodyCount > 0);
    --this.m_bodyCount;

    //b->~b2Body();
    //b.Destroy();
    // Add to the deferred destruction list.
    b.m_prev = null;
    b.m_next = this.m_bodyDestroyList;
    this.m_bodyDestroyList = b;
  },

  CleanBodyList: function () {
    this.m_contactManager.m_destroyImmediate = true;

    var b = this.m_bodyDestroyList;
    while (b) {
      //$.Engine.Settings.b2Assert((b.m_flags & $.Engine.Dynamic.Body.e_destroyFlag) != 0);

      // Preserve the next pointer.
      var b0 = b;
      b = b.m_next;

      // Delete the attached joints
      var jn = b0.m_jointList;
      while (jn) {
        var jn0 = jn;
        jn = jn.next;

        if (this.m_listener) {
          this.m_listener.NotifyJointDestroyed(jn0.joint);
        }

        this.DestroyJoint(jn0.joint);
      }

      b0.Destroy();
      //this.m_blockAllocator.Free(b0, sizeof(b2Body));
    }

    // Reset the list.
    this.m_bodyDestroyList = null;

    this.m_contactManager.m_destroyImmediate = false;
  },

  step: new $.Engine.Dynamic.TimeStep(),
  // this.Step
  Step: function (dt, iterations) {

    var b;
    var other;


    this.step.dt = dt;
    this.step.iterations = iterations;
    if (dt > 0.0) {
      this.step.inv_dt = 1.0 / dt;
    }
    else {
      this.step.inv_dt = 0.0;
    }

    this.m_positionIterationCount = 0;

    // Handle deferred contact destruction.
    this.m_contactManager.CleanContactList();

    // Handle deferred body destruction.
    this.CleanBodyList();

    // Update contacts.
    this.m_contactManager.Collide();

    // Size the island for the worst case.
    var island = new $.Engine.Dynamic.Island(this.m_bodyCount, this.m_contactCount, this.m_jointCount, this.m_stackAllocator);

    // Clear all the island flags.
    for (b = this.m_bodyList; b != null; b = b.m_next) {
      b.m_flags &= ~$.Engine.Dynamic.Body.e_islandFlag;
    }
    for (var c = this.m_contactList; c != null; c = c.m_next) {
      c.m_flags &= ~$.Engine.Dynamic.Contact.Contact.e_islandFlag;
    }
    for (var j = this.m_jointList; j != null; j = j.m_next) {
      j.m_islandFlag = false;
    }

    // Build and simulate all awake islands.
    var stackSize = this.m_bodyCount;
    //var stack = (b2Body**)this.m_stackAllocator.Allocate(stackSize * sizeof(b2Body*));
    var stack = new Array(this.m_bodyCount);
    for (var k = 0; k < this.m_bodyCount; k++)
      stack[k] = null;

    for (var seed = this.m_bodyList; seed != null; seed = seed.m_next) {
      if (seed.m_flags & ($.Engine.Dynamic.Body.e_staticFlag | $.Engine.Dynamic.Body.e_islandFlag | $.Engine.Dynamic.Body.e_sleepFlag | $.Engine.Dynamic.Body.e_frozenFlag)) {
        continue;
      }

      // Reset island and stack.
      island.Clear();
      var stackCount = 0;
      stack[stackCount++] = seed;
      seed.m_flags |= $.Engine.Dynamic.Body.e_islandFlag;

      // Perform a depth first search (DFS) on the constraint graph.
      while (stackCount > 0) {
        // Grab the next body off the stack and add it to the island.
        b = stack[--stackCount];
        island.AddBody(b);

        // Make sure the body is awake.
        b.m_flags &= ~$.Engine.Dynamic.Body.e_sleepFlag;

        // To keep islands, we don't
        // propagate islands across static bodies.
        if (b.m_flags & $.Engine.Dynamic.Body.e_staticFlag) {
          continue;
        }

        // Search all contacts connected to this body.
        for (var cn = b.m_contactList; cn != null; cn = cn.next) {
          if (cn.contact.m_flags & $.Engine.Dynamic.Contact.Contact.e_islandFlag) {
            continue;
          }

          island.AddContact(cn.contact);
          cn.contact.m_flags |= $.Engine.Dynamic.Contact.Contact.e_islandFlag;

          other = cn.other;
          if (other.m_flags & $.Engine.Dynamic.Body.e_islandFlag) {
            continue;
          }

          //$.Engine.Settings.b2Assert(stackCount < stackSize);
          stack[stackCount++] = other;
          other.m_flags |= $.Engine.Dynamic.Body.e_islandFlag;
        }

        // Search all joints connect to this body.
        for (var jn = b.m_jointList; jn != null; jn = jn.next) {
          if (jn.joint.m_islandFlag == true) {
            continue;
          }

          island.AddJoint(jn.joint);
          jn.joint.m_islandFlag = true;

          other = jn.other;
          if (other.m_flags & $.Engine.Dynamic.Body.e_islandFlag) {
            continue;
          }

          //$.Engine.Settings.b2Assert(stackCount < stackSize);
          stack[stackCount++] = other;
          other.m_flags |= $.Engine.Dynamic.Body.e_islandFlag;
        }
      }

      island.Solve(this.step, this.m_gravity);

      this.m_positionIterationCount = $.Engine.Math.Max(this.m_positionIterationCount, $.Engine.Dynamic.Island.m_positionIterationCount);

      if (this.m_allowSleep) {
        island.UpdateSleep(dt);
      }

      // Post solve cleanup.
      for (var i = 0; i < island.m_bodyCount; ++i) {
        // Allow static bodies to participate in other islands.
        b = island.m_bodies[i];
        if (b.m_flags & $.Engine.Dynamic.Body.e_staticFlag) {
          b.m_flags &= ~$.Engine.Dynamic.Body.e_islandFlag;
        }

        // Handle newly frozen bodies.
        if (b.IsFrozen() && this.m_listener) {
          var response = this.m_listener.NotifyBoundaryViolated(b);
          if (response == $.Engine.Dynamic.WorldListener.b2_destroyBody) {
            this.DestroyBody(b);
            b = null;
            island.m_bodies[i] = null;
          }
        }
      }
    }

    this.m_broadPhase.Commit();

    //this.m_stackAllocator.Free(stack);
  },

  // this.Query the world for all shapes that potentially overlap the
  // provided AABB. You provide a shape pointer buffer of specified
  // size. The number of shapes found is returned.
  Query: function (aabb, shapes, maxCount) {

    //void** results = (void**)this.m_stackAllocator.Allocate(maxCount * sizeof(void*));
    var results = new Array();
    var count = this.m_broadPhase.QueryAABB(aabb, results, maxCount);

    for (var i = 0; i < count; ++i) {
      shapes[i] = results[i];
    }

    //this.m_stackAllocator.Free(results);
    return count;
  },

  // You can use these to iterate over all the bodies, joints, and contacts.
  GetBodyList: function () {
    return this.m_bodyList;
  },
  GetJointList: function () {
    return this.m_jointList;
  },
  GetContactList: function () {
    return this.m_contactList;
  },

  //--------------- Internals Below -------------------

  m_blockAllocator: null,
  m_stackAllocator: null,

  m_broadPhase: null,
  m_contactManager: new $.Engine.Dynamic.ContactManager(),

  m_bodyList: null,
  m_contactList: null,
  m_jointList: null,

  m_bodyCount: 0,
  m_contactCount: 0,
  m_jointCount: 0,

  // These bodies will be destroyed at the next time this.step.
  m_bodyDestroyList: null,

  m_gravity: null,
  m_allowSleep: null,

  m_groundBody: null,

  m_listener: null,
  m_filter: null,

  m_positionIterationCount: 0
});
$.Engine.Dynamic.World.s_enablePositionCorrection = 1;
$.Engine.Dynamic.World.s_enableWarmStarting = 1;